﻿using mozhi.smarterp.Finance.Enums;
using Mozhi.Abp.Domain.BasicData;
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp;

namespace mozhi.smarterp.Finance.Treasuries
{
    /// <summary>
    /// 付款单
    /// </summary>
    public class Payment : DocumentAggregateRoot
    {
        public PaymentType Type { private set; get; }

        public TradingPartnerType TradingPartnerType { private set; get; }

        public Guid? SupplierId { private set; get; }

        public Guid? EmployeeId { private set; get; }

        public Guid? CustomerId { private set; get; }

        public Guid? BankAccountId { private set; get; }

        public Guid CurrencyId { private set; get; }

        public Currency Currency { private set; get; }

        public decimal ExchangeRate { private set; get; }

        public decimal Amount { private set; get; }

        public decimal AmountFr { private set; get; }

        /// <summary>
        /// 是否预付款
        /// </summary>
        public bool IsAdvanced { private set; get; }

        /// <summary>
        /// 是否退款
        /// </summary>
        public bool IsRefund { private set; get; }


        public List<PaymentItem> Items { private set; get; }

        private Payment() { }

        public Payment(Guid id, string number, DateTime date, Guid? bankAccountId, Guid? supplierId, Guid? employeeId, Guid? customerId, TradingPartnerType tradingPartnerType,
            Guid currencyId, decimal exchangeRate, bool isAdvanced, bool isRefund, string comment, DocumentStatusType status)
        {
            Id = id;
            Number = number;
            Date = date;
            SupplierId = supplierId;
            EmployeeId = employeeId;
            CustomerId = customerId;
            CurrencyId = currencyId;
            ExchangeRate = exchangeRate;
            BankAccountId = bankAccountId;
            TradingPartnerType = tradingPartnerType;
            IsAdvanced = isAdvanced;
            IsRefund = isRefund;
            Comment = comment;

            SetStatus(status);

            Items = new List<PaymentItem>();

            Validate();
        }

        public void Change(DateTime date, Guid? bankAccountId, Guid? supplierId, Guid? employeeId, Guid? customerId, TradingPartnerType tradingPartnerType,
            Guid currencyId, decimal exchangeRate, bool isAdvanced, bool isRefund)
        {
            Date = date;
            SupplierId = supplierId;
            EmployeeId = employeeId;
            CustomerId = customerId;
            CurrencyId = currencyId;
            ExchangeRate = exchangeRate;
            BankAccountId = bankAccountId;
            TradingPartnerType = tradingPartnerType;
            IsAdvanced = isAdvanced;
            IsRefund = isRefund;

            Validate();
        }


        public void SetItems(List<PaymentItem> items)
        {
            if ((TradingPartnerType == TradingPartnerType.Supplier || TradingPartnerType == TradingPartnerType.Customer)
                && items.Exists(x => x.ExpenseItemId.HasValue))
                throw new UserFriendlyException("交易对象为供应商，不允许填写费用项目", "100000");

            if (TradingPartnerType == TradingPartnerType.Employee && items.Exists(x => x.ProductId.HasValue))
                throw new UserFriendlyException("交易对象为员工，不允许填写产品信息", "100000");

            foreach (var item in items)
            {
                AddOrUpdateItem(item);
            }

            var ids = items.Select(x => x.Id).ToList();

            var removedItems = Items.Where(x => !ids.Contains(x.Id)).ToList();

            foreach (var item in removedItems)
            {
                Items.Remove(item);
            }

            CalculateAmount();

        }


        #region
        private void Validate()
        {
            if (Number.IsNullOrEmpty())
                throw new UserFriendlyException("编号不能为空", "100000");

            if (CurrencyId == default)
                throw new UserFriendlyException("币别不能为空", "100000");

            if (ExchangeRate <= 0)
                throw new UserFriendlyException("汇率必须大于0", "100000");

            if (Date == default)
                throw new UserFriendlyException("币别不能为空", "100000");

            if (!SupplierId.HasValue && !EmployeeId.HasValue && !CustomerId.HasValue)
                throw new UserFriendlyException("交易对象不能为空", "100000");

            if (TradingPartnerType != TradingPartnerType.Supplier && TradingPartnerType != TradingPartnerType.Employee)
                throw new UserFriendlyException("交易对象只能是供应商，客户或者员工", "100000");

            if (IsAdvanced == true && TradingPartnerType != TradingPartnerType.Supplier)
                throw new UserFriendlyException("预付款的交易对象只能是供应商", "100000");

            if (IsRefund == true && TradingPartnerType == TradingPartnerType.Supplier)
                throw new UserFriendlyException("退款单的交易对象不能是供应商", "100000");

            if (IsRefund == false && TradingPartnerType == TradingPartnerType.Customer)
                throw new UserFriendlyException("付款单的交易对象不能是客户", "100000");

            if (IsRefund == true && IsAdvanced == true)
                throw new UserFriendlyException("退款单不能是预付单", "100000");

            if (TradingPartnerType == TradingPartnerType.Supplier && (EmployeeId.HasValue || CustomerId.HasValue))
                throw new UserFriendlyException("交易对象为供应商时，只能填写供应商信息", "100000");

            if (TradingPartnerType == TradingPartnerType.Employee && (SupplierId.HasValue || CustomerId.HasValue))
                throw new UserFriendlyException("交易对象为员工时，只能填写员工信息", "100000");

            if (TradingPartnerType == TradingPartnerType.Customer && (SupplierId.HasValue || EmployeeId.HasValue))
                throw new UserFriendlyException("交易对象为客户时，只能填写客户信息", "100000");

            if (Status == DocumentStatusType.Approved && BankAccountId == default)
                throw new UserFriendlyException("银行账号不能为空");
        }


        private void CalculateAmount()
        {
            foreach (var item in Items)
            {
                item.CalculateAmount(ExchangeRate);
            }

            Amount = Items.Sum(x => x.Amount);
            AmountFr = Items.Sum(x => x.AmountFr);
        }

        private void AddOrUpdateItem(PaymentItem item)
        {
            ValidateStatus();

            var match = Items.FirstOrDefault(x => x.Id == item.Id);

            if (match == null)
            {
                match = item;

                match.CalculateAmount(ExchangeRate);

                Items.Add(match);
            }
            else
            {
                match.Change(item.Product, item.ExpenseItem, item.AmountFr, item.Amount, item.Remark);

                item.CalculateAmount(ExchangeRate);
            }
        }

        #endregion
    }


    public class PaymentItem : DocumentEntity
    {
        public Guid? ProductId { get; private set; }

        public Product Product { get; private set; }

        public Guid? ExpenseItemId { get; private set; }

        public ExpenseItem ExpenseItem { get; private set; }

        private PaymentItem() { }

        public PaymentItem(Guid id, Product product, ExpenseItem expenseItem, decimal amountFr, decimal? amount, string remark)
        {
            Id = id;

            Product = product;
            ExpenseItem = expenseItem;

            ProductId = product?.Id;
            ExpenseItemId = expenseItem?.Id;

            AmountFr = amountFr;
            Amount = amount ?? 0;

            Remark = remark;

            Validate();
        }

        public void Change(Product product, ExpenseItem expenseItem, decimal amountFr, decimal? amount, string remark)
        {
            Product = product;
            ExpenseItem = expenseItem;

            ProductId = product?.Id;
            ExpenseItemId = expenseItem?.Id;

            AmountFr = amountFr;
            Amount = amount ?? 0;

            Remark = remark;

            Validate();
        }

        public void CalculateAmount(decimal exchangeRate)
        {
            if (Amount == 0)
                Amount = AmountFr * exchangeRate;
        }

        #region
        private void Validate()
        {
            if (AmountFr <= 0)
                throw new UserFriendlyException("付款金额必须大于0", "100000");
        }
        #endregion
    }
}
